#ifndef BLOCKQUEUE_H 
#define BLOCKQUEUE_H 

#include <mutex>
#include <deque>
#include <condition_variable> 
#include <assert.h>
#include <chrono>

template <class T>
class CBlockQueue
{
public:
    explicit CBlockQueue(size_t maxCapacity = 1024);
    ~CBlockQueue();
    void close();
    void flush();
    void clear();
    T front();
    T back();
    size_t getSize();
    size_t getCapacity();
    void  PushBack(const T& item);
    void  PushFront(const T& item);
    bool empty();
    bool full();
    bool pop(T& item);
    bool pop(T& item, int timeout);

private:
    std::deque<T> m_deq;
    size_t m_capacity;
    std::mutex m_mtx;
    bool m_isClose;
    std::condition_variable m_Consumer;
    std::condition_variable m_Producer;
};
template<class T>
CBlockQueue<T>::CBlockQueue(size_t maxCapacity):m_capacity( maxCapacity)
{
    assert(maxCapacity > 0);
    m_isClose = false;
};
template<class T>
CBlockQueue<T>::~CBlockQueue()
{
   close();
};
template<class T>
void CBlockQueue<T>::close()
{
   std::lock_guard<std::mutex> locker(m_mtx);
   m_deq.clear();
   m_isClose = true;
   m_Producer.notify_all();
   m_Consumer.notify_all();
};
template<class T>
void CBlockQueue<T>::clear()
{
    std::lock_guard<std::mutex> lock(m_mtx);
    m_deq.clear();
};
template<class T>
T  CBlockQueue<T>::front()
{
    std::lock_guard<std::mutex> lock(m_mtx);
    return m_deq.front();
};
template<class T>
T CBlockQueue<T>::back()
{
    std::lock_guard<std::mutex> lock(m_mtx);
    return m_deq.back();
};
template<class T>
size_t CBlockQueue<T>::getSize()
{
 std::lock_guard<std::mutex> lock(m_mtx);
 return m_deq.size();   
}
template<class T>
size_t  CBlockQueue<T>::getCapacity()
{
    std::lock_guard<std::mutex> lock(m_mtx);
    return m_capacity;
};
template<class T>
void CBlockQueue<T>::PushBack(const T& item)
{
    std::unique_lock<std::mutex> lock(m_mtx);
    while(m_deq.size() >= m_capacity)
    {
        m_Producer.wait(lock);
    }
    m_deq.push_back(item);
    m_Consumer.notify_all();
};
template<class T>
void CBlockQueue<T>::PushFront(const T& item)
{
    std::unique_lock<std::mutex> lock(m_mtx);
    while(m_deq.size() >= m_capacity)
    {
        m_Producer.wait(lock);
    }
    m_deq.push_front(item);
    m_Consumer.notify_one();
};
template <class T>
bool  CBlockQueue<T>::empty()
{
    std::lock_guard<std::mutex> lock(m_mtx);
    return m_deq.empty();
};
template<class T>
bool  CBlockQueue<T>::full()
{
    std::lock_guard<std::mutex> lock(m_mtx);
    return m_deq.size() >= m_capacity;
};
template <class T>
bool  CBlockQueue<T>::pop(T& item)
{   
    std::unique_lock<std::mutex> lock(m_mtx);
    while(m_deq.empty())
    {
        m_Consumer.wait(lock);
        if(m_isClose)
             return false;
    }
    item = m_deq.front();
    m_deq.pop_front();
    m_Producer.notify_one();
    return true;
};
template<class T>
bool CBlockQueue<T>::pop(T& item, int timeout)
{
    std::unique_lock<std::mutex> lock(m_mtx);
    while(m_deq.empty())
    {
        if(m_Consumer.wait_for(lock,std::chrono::microseconds(timeout))== std::cv_status::timeout)
        {
            return false;
        }
        if(m_isClose)
        {
            return false;
        }
    }
    item = m_deq.front();
    m_deq.pop_front();
    m_Producer.notify_one();
    return true;
}
#endif